-module(player).
-behaviour(gen_server).

-export([init/1, handle_call/3, handle_cast/2,
         handle_info/2, terminate/2, code_change/3]).

-export([start/1, stop/1, stop/2]).

-export([create/2]).

-include_lib("eunit/include/eunit.hrl").

-include("common.hrl").
-include("pp.hrl").
-include("schema.hrl").

-record(pdata, {
                pid,
                socket = none,
                %% game to inplay cross-reference for this player
                playing = gb_trees:empty(),
                watching = gb_trees:empty(),
                zombie = 0, % % on autoplay until game ends
                self
               }).

start(Nick)
  when is_binary(Nick) ->
    %% make sure we exist
    case db:index_read(tab_player_info, Nick, #tab_player_info.nick) of
        [Info] ->
            PID = Info#tab_player_info.pid,
            gen_server:start({global, {player, PID}}, player, [PID], []);
        Any ->
            {error, Any}
    end.

init([PID])
  when is_integer(PID) ->
    process_flag(trap_exit, true),
    ok = create_runtime(PID, self()),
    {ok, #pdata{ pid = PID, self = self() }}.

stop(Player)
  when is_pid(Player) ->
    gen_server:cast(Player, stop).

stop(Player, Reason)
  when is_pid(Player) ->
    gen_server:cast(Player, {stop, Reason}).

terminate(_Reason, Data) ->
    ok = db:delete(tab_player, Data#pdata.pid).

handle_cast('DISCONNECT', Data) ->
    {noreply, Data};

handle_cast({'SOCKET', Socket}, Data) ->
    Data1 = Data#pdata{ socket = Socket },
    {noreply, Data1};


handle_cast(#logout{}, Data) ->
    forward_to_client(logout, Data),
    {noreply, Data}; 

handle_cast(stop, Data) ->
    {stop, normal, Data};

handle_cast({stop, Reason}, Data) ->
    {stop, Reason, Data};

handle_cast(Event, Data) ->
    error_logger:info_report([{module, ?MODULE},
                              {line, ?LINE},
                              {self, self()},
                              {message, Event}
                             ]),
    {noreply, Data}.

handle_call('ID', _From, Data) ->
    {reply, Data#pdata.pid, Data};

handle_call('SOCKET', _From, Data) ->
    {reply, Data#pdata.socket, Data};

handle_call('GAMES', _From, Data) ->
    {reply, gb_trees:keys(Data#pdata.playing), Data};

handle_call(Event, From, Data) ->
    error_logger:info_report([{module, ?MODULE},
                              {line, ?LINE},
                              {self, self()},
                              {message, Event},
                              {from, From}
                             ]),
    {noreply, Data}.

handle_info({'EXIT', _Pid, _Reason}, Data) ->
    %% child exit?
    {noreply, Data};

handle_info(Info, Data) ->
    error_logger:info_report([{module, ?MODULE},
                              {line, ?LINE},
                              {self, self()},
                              {message, Info}]),
    {noreply, Data}.

code_change(_OldVsn, Data, _Extra) ->
    {ok, Data}.


create(Nick, Pass)
  when is_list(Nick),
       is_list(Pass) ->
    create(list_to_binary(Nick),
           list_to_binary(Pass));

create(Nick, Pass)
  when is_binary(Nick),
       is_binary(Pass) ->
    case db:index_read(tab_player_info, Nick, #tab_player_info.nick) of
        [_] ->
            {error, player_exists};
        _ ->
            ID = counter:bump(player),
            Info = #tab_player_info {
                                     pid = ID,
                                     nick = Nick,
                                     %% store a hash of the password
                                     %% instead of the password itself
                                     password = erlang:phash2(Pass, 1 bsl 32)                                     
                                    },
            ok = db:write(Info),
            gen_server:start({global, {player, ID}}, player, [ID], []),
            {ok, ID}
    end.

create_runtime(ID, Pid)
  when is_number(ID),
       is_pid(Pid) ->
    Player = #tab_player {
                          pid = ID,
                          process = Pid
                         },
    ok = db:write(Player).


%% delete_balance(PID) ->
%% db:delete(tab_balance, PID).

%% update_balance(PID, Amount) ->
%% db:update_balance(tab_balance, PID, Amount).

forward_to_client(Event, Data) ->
    if
        Data#pdata.socket /= none ->
            Data#pdata.socket ! {packet, Event};
        true ->
            ok
    end.
